package zero.tech.core.work;

import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingDeque;

import zero.tech.core.find.DynamicFind;
import zero.tech.core.util.EngineLogUtil;

public class WorkManager extends DynamicFind {
	private static WorkManager workManager = new WorkManager();
	private final Map<Long, DelayWorker> delayworkers;
	private final Map<Long, Worker> workers;
	private final ExecutorService executor;

	public static WorkManager getManager() {
		return workManager;
	}

	private WorkManager() {
		this.executor = Executors.newFixedThreadPool(100);
		this.workers = new ConcurrentHashMap<Long, Worker>();
		this.delayworkers=new ConcurrentHashMap<Long,DelayWorker>();
	}

	public void submit(Work work, Object... objs) throws Exception {
		work.init(objs);
		submit(work);
	}

	public void submit(Class<? extends Work> workClass, Object... objs){
		Class<? extends Work> clz =workClass;
		Work work=null;
		try {
			work = clz.newInstance();
			work.init(objs);
			submit(work);
		}catch (Exception e) {
			EngineLogUtil.traceFull(e);
		}
	}

	public void submit(Work work) {
		if (work instanceof AynWork) {
			AynWork aynWork = (AynWork) work;
			try {
				executor.execute(aynWork);
			} catch (Exception e) {
				EngineLogUtil.traceFull(e);
			}
		}else if(work instanceof DelayQueueWork) {
			DelayQueueWork delayWork = (DelayQueueWork) work;
			DelayWorkQueue workQueue = delayWork.getDelayQueue();
			long queue = workQueue.getId();
			synchronized (delayworkers) {
				DelayWorker worker = delayworkers.get(queue);
				if (worker == null) {
					worker = createDelayWorker(queue);
					worker.getDelayworks().offer(delayWork);
					executor.execute(worker);
					return;
				}
				worker.getDelayworks().offer(delayWork);
			}
		}
		else if (work instanceof QueueWork) {
			QueueWork queueWork = (QueueWork) work;
			WorkQueue workQueue = queueWork.getWorkQueue();
			long queue = workQueue.getId();
			synchronized (workers) {
				Worker worker = workers.get(queue);
				if (worker == null) {
					worker = createWorker(queue);
					worker.getQueueWorks().offer(queueWork);
					executor.execute(worker);
					return;
				}
				worker.getQueueWorks().offer(queueWork);
			}
		} else if (work instanceof Runnable) {
			Runnable r = (Runnable) work;
			try {
				executor.execute(r);
			} catch (Exception e) {
				EngineLogUtil.traceFull(e);
			}
		} else {
			throw new RuntimeException(work.getClass().getSimpleName() + " work's type can not found");
		}
	}

	private DelayWorker createDelayWorker(long queue) {
		DelayWorker worker = null;
		worker = new DelayWorker(queue);
		delayworkers.put(queue, worker);
		return worker;
	}

	private Worker createWorker(long workQueue) {
		Worker worker = null;
		worker = new Worker(workQueue);
		workers.put(workQueue, worker);
		return worker;
	}

	private class Worker extends AynWork {
		private final long workQueue;
		private final LinkedBlockingDeque<QueueWork> queueWorks;

		@SuppressWarnings("unused")
		public long getWorkQueue() {
			return workQueue;
		}

		public Worker(long workQueue) {
			this.workQueue = workQueue;
			queueWorks = new LinkedBlockingDeque<QueueWork>();
		}

		public LinkedBlockingDeque<QueueWork> getQueueWorks() {
			return queueWorks;
		}

		@Override
		public void init(Object... objs) throws Exception {
		}

		@Override
		public void run() {
			while (true) {
				QueueWork queueWork = null;
				try {
					queueWork = queueWorks.take();
					queueWork.run();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	
	private class DelayWorker extends AynWork {
		private final long workQueue;
		private final DelayQueue<DelayQueueWork> delayworks;

		public DelayQueue<DelayQueueWork> getDelayworks() {
			return delayworks;
		}
		@SuppressWarnings("unused")
		public long getWorkQueue() {
			return workQueue;
		}

		public DelayWorker(long workQueue) {
			this.workQueue = workQueue;
			delayworks=new DelayQueue<>();
		}

		@Override
		public void init(Object... objs) throws Exception {
		}

		@Override
		public void run() {
			while (true) {
				DelayQueueWork delayWork = null;
				try {
					delayWork = delayworks.take();
					delayWork.run();
				} catch (Exception e) {
					EngineLogUtil.traceFull(e);
				}
			}
		}
	}

	@Override
	public void findClass(Class<?> clazz) throws Exception {
	}

	@Override
	public boolean verification(Class<?> clazz) {
		return superClassOn(clazz, QueueWork.class) || superClassOn(clazz, AynWork.class);
	}

	@SuppressWarnings("unchecked")
	public <T> T submitCallable(Class<?> clz, Object... objs) throws Exception{
		try {
			Callable<T> callable = (Callable<T>) clz.newInstance();
			if (callable instanceof Work) {
				Work work = (Work) callable;
				work.init(objs);
				Future<T> future = this.executor.submit((Callable<T>) work);
				return future.get();
			} else {
				throw new RuntimeException("must implements Work interface!!!");
			}
		} catch (Exception e) {
			EngineLogUtil.traceFull(e);
			throw e;
		}
	}

	public <T> T submitCallable(Callable<T> call) throws Exception {
		Future<T> future = this.executor.submit(call);
		return future.get();
	}

	@Override
	public void afterFind() {
	}

	@Override
	public void beforeFind() {
	}
}
